/*
 * ========THE SOLMIX PROJECT=====================================
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */

package org.solmix.fmk.servlet;

import java.io.IOException;
import java.io.Writer;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.oro.text.perl.Perl5Util;
import org.solmix.api.exception.SLXException;
import org.solmix.api.serialize.JSParser;
import org.solmix.api.serialize.JSParserFactory;
import org.solmix.api.types.Texception;
import org.solmix.api.types.Tmodule;
import org.solmix.commons.collections.DataTypeMap;
import org.solmix.commons.logs.Logger;
import org.solmix.fmk.internel.DSConfigManager;
import org.solmix.fmk.serialize.JSParserFactoryImpl;

/**
 * @author solomon
 * @since 0.0.1
 * @version $ID$ 2010-12-31 solmix-ds
 */
public class RequestContext
{

    /**
     * @return the request
     */
    public SLXHttpServletRequest getRequest() {
        return request;
    }

    /**
     * @param request the request to set
     */
    public void setRequest(SLXHttpServletRequest request) {
        this.request = request;
    }

    /**
     * @return the response
     */
    public HttpServletResponse getResponse() {
        return response;
    }

    /**
     * @param response the response to set
     */
    public void setResponse(HttpServletResponse response) {
        this.response = response;
    }

    /**
     * @return the pathInfo
     */
    public String getPathInfo() {
        return pathInfo;
    }

    /**
     * @param pathInfo the pathInfo to set
     */
    public void setPathInfo(String pathInfo) {
        this.pathInfo = pathInfo;
    }

    /**
     * @return the clientContextHeaderSent
     */
    public boolean isClientContextHeaderSent() {
        return clientContextHeaderSent;
    }

    /**
     * @param clientContextHeaderSent the clientContextHeaderSent to set
     */
    public void setClientContextHeaderSent(boolean clientContextHeaderSent) {
        this.clientContextHeaderSent = clientContextHeaderSent;
    }

    /**
     * @return the cachingEnabled
     */
    public boolean isCachingEnabled() {
        return cachingEnabled;
    }

    /**
     * @param cachingEnabled the cachingEnabled to set
     */
    public void setCachingEnabled(boolean cachingEnabled) {
        this.cachingEnabled = cachingEnabled;
    }

    private static DataTypeMap globalConfig;

    private static Logger log = new Logger(RequestContext.class.getName());

    public static Logger staticLog;

    public ServletContext servletContext;

    public SLXHttpServletRequest request;

    public HttpServletResponse response;

    public HttpSession session;

    public String contentType;

    public String requestPath;

    public String servletPath;

    public String pathInfo;

    protected Writer out;

    public boolean clientContextHeaderSent;

    public boolean cachingEnabled;

    private JSParser jsParser;

    public static RequestContext instance(ServletRequest request, ServletResponse response) throws Exception {
        return instance((ServletContext) null, ((request)), ((response)));
    }

    public static RequestContext instance(Servlet servlet, HttpServletRequest request, HttpServletResponse response) throws SLXException {
        return instance(servlet.getServletConfig().getServletContext(), request, response);
    }

    public static RequestContext instance(Servlet servlet, HttpServletRequest request, HttpServletResponse response, Writer out) throws SLXException {
        RequestContext context = instance(servlet.getServletConfig().getServletContext(), request, response);
        context.setOut(out);
        return context;
    }

    /**
     * @param servletContext
     * @param request
     * @param response
     * @return
     * @throws SLXException
     */
    public static RequestContext instance(ServletContext servletContext, ServletRequest request, ServletResponse response) throws SLXException {
        RequestContext context = new RequestContext();
        context.init(servletContext, (HttpServletRequest) request, (HttpServletResponse) response);
        return context;
    }

    public RequestContext()
    {
        out = null;
        clientContextHeaderSent = false;
        cachingEnabled = true;
        contentType = null;
    }

    public RequestContext(Servlet servlet, HttpServletRequest request, HttpServletResponse response) throws SLXException
    {
        out = null;
        clientContextHeaderSent = false;
        cachingEnabled = true;
        contentType = null;
        init(servlet.getServletConfig().getServletContext(), request, response);
    }

    /**
     * Return the ServletResponse writer.if the content type is null set "text/html"
     * 
     * @return the out
     * @throws SLXSLXException
     */
    public Writer getOut() throws SLXException {
        if (out == null) {
            if (log.isDebugEnabled())
                log.debug("Getting output stream via servletResponse.getWriter()");
            if (contentType == null)
                setContentType(globalConfig.getString("defaultMimeType", "text/html"));
            try {
                setOut(response.getWriter());
            } catch (IOException e) {
                throw new SLXException(Tmodule.SERVLET, Texception.IO_EXCEPTION, "ioexception with response.getWriter()", e);
            }
        }
        return out;
    }

    /**
     * @param out the out to set
     */
    public void setOut(Writer out) {
        this.out = out;
    }

    /**
     * @param request
     * @return
     */
    public static String getRequestPath(HttpServletRequest request) {
        String requestPath = request.getRequestURI();
        if (requestPath.indexOf("//") != -1) {
            Perl5Util regex = new Perl5Util();
            requestPath = regex.substitute("s#//#/#g", requestPath);
        }
        return requestPath;
    }

    /**
     * @param servletContext
     * @param request
     * @param response
     * @throws SLXException
     */
    public void init(ServletContext servletContext, HttpServletRequest request, HttpServletResponse response) throws SLXException {
        JSParserFactory jsFactory = new JSParserFactoryImpl();
        jsParser = jsFactory.get();
        this.servletContext = servletContext;
        globalConfig = DSConfigManager.getConfig().getSubtree("request");
        if (globalConfig == null)
            globalConfig = new DataTypeMap();
        requestPath = getRequestPath(request);
        this.session = request.getSession();
        servletPath = request.getServletPath();
        pathInfo = request.getPathInfo();
        this.request = new SLXHttpServletRequest(request);
        this.response = response;
        if (globalConfig.getBoolean("reportParams", false)) {
            if (log.isDebugEnabled())
                log.debug((new StringBuilder()).append("Request parameters: ").append(getParamsAsString()).toString());
        }
        if (log.isInfoEnabled()) {
            String cgetHeader = request.getHeader("If-Modified-Since");
            boolean isCGET = cgetHeader != null && !"".equals(cgetHeader);
            boolean alreadyLoggedURL = request.getAttribute("isc_alreadyLoggedURL") != null;
            if ((!isCGET || log.isDebugEnabled()) && !alreadyLoggedURL) {
                request.setAttribute("isc_alreadyLoggedURL", new Object());
                log.info((new StringBuilder()).append(isCGET ? "CGET " : "").append("URL: '").append(requestPath).append("'").append(
                    ", User-Agent: '").append(request.getHeader("User-Agent")).append("'").append(": ").append(ServletTools.getBrowserSummary(this)).toString());
            }
        }
        if (globalConfig.getBoolean("logCookies", false))
            logCookies();
        if (globalConfig.getBoolean("logHeaders", false))
            logHeaders();
        if (staticLog == null)
            staticLog = new Logger(RequestContext.class.getName());
    }

    /**
     * @return the servletContext
     */
    public ServletContext getServletContext() {
        return servletContext;
    }

    /**
     * @param servletContext the servletContext to set
     */
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    /**
     * @return the session
     */
    public HttpSession getSession() {
        return session;
    }

    /**
     * @param session the session to set
     */
    public void setSession(HttpSession session) {
        this.session = session;
    }

    /**
     * @return the contentType
     */
    public String getContentType() {
        return contentType;
    }

    /**
     * @param contentType the contentType to set
     */
    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    /**
     * @return the requestPath
     */
    public String getRequestPath() {
        return requestPath;
    }

    /**
     * @param requestPath the requestPath to set
     */
    public void setRequestPath(String requestPath) {
        this.requestPath = requestPath;
    }

    /**
     * @return the servletPath
     */
    public String getServletPath() {
        return servletPath;
    }

    /**
     * @param servletPath the servletPath to set
     */
    public void setServletPath(String servletPath) {
        this.servletPath = servletPath;
    }

    public void logCookies() {
        if (log.isInfoEnabled())
            log.info(getCookiesAsString());
    }

    public void logHeaders() {
        if (log.isInfoEnabled())
            log.info(getHeadersAsString());
    }

    /**
     * Parser Headers as a String pair .Like:
     * 
     * <pre>
     * headerName = value
     * </pre>
     * 
     * @return
     */
    public String getHeadersAsString() {
        String output = "Client HTTP Headers:";
        Enumeration<?> headers = request.getHeaderNames();
        if (headers == null)
            output = (new StringBuilder()).append(output).append(" request.getHeaderNames() returned null").toString();
        else
            while (headers.hasMoreElements()) {
                String headerName = (String) headers.nextElement();
                output = (new StringBuilder()).append(output).append("\n").append(headerName).append(": ").append(request.getHeader(headerName)).toString();
            }
        return output;
    }

    /**
     * Parser Cookies as a String pair .Like:
     * 
     * <pre>
     * Name:'name1',Value='vlaue1', domain:'..' , path:'..' , maxAge:'..' , isSecure:'..'
     * </pre>
     * 
     * @return
     */
    public String getCookiesAsString() {
        String output = "Cookies:";
        Cookie cookies[] = request.getCookies();
        if (cookies == null) {
            output = (new StringBuilder()).append(output).append(" NONE").toString();
            return output;
        }
        for (int ii = 0; ii < cookies.length; ii++)
            output = (new StringBuilder()).append(output).append("\nName: '").append(cookies[ii].getName()).append("', value: '").append(
                cookies[ii].getValue()).append("', domain: '").append(cookies[ii].getDomain()).append("', path: '").append(cookies[ii].getPath()).append(
                "', maxAge: '").append(cookies[ii].getMaxAge()).append("', isSecure: '").append(cookies[ii].getSecure()).append("'").toString();

        return output;
    }

    public String getParamsAsString() throws SLXException {
        return jsParser.toJavaScript(request.getParams());
    }

    public void setNoCacheHeaders() throws SLXException {
        if (response.isCommitted())
            throw new SLXException(Tmodule.SERVLET, Texception.SERVLET_REQ_ALREADY_COMMITED,
                "Response has already been committed, unable to setNoCacheHeaders()");
        if (cachingEnabled) {
            setNoCacheHeaders(response);
            cachingEnabled = false;
        }
    }

    public static void setNoCacheHeaders(HttpServletResponse response) {
        if (log.isDebugEnabled())
            log.debug("Setting headers to disable caching");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", System.currentTimeMillis());
    }
}
